#!/usr/bin/env python

"""
A simple OAuth implementation for authenticating users with microsoft Live

"""

from google.appengine.api import memcache
from google.appengine.api import urlfetch
from urllib import quote

import json
from urllib import urlencode


import logging
from datetime import datetime 


class SkyDriveClient():
  """SkyDrive Client.
  A client for talking to the SkyDrive API using OAuth as the authentication model.
  Authorization flow:
      http://msdn.microsoft.com/en-us/library/live/hh243647.aspx#authcodegrant
  SkyDrive API:
      http://msdn.microsoft.com/zh-CN/library/live/hh826545#dir
  Personal API:
      http://msdn.microsoft.com/zh-cn/library/live/hh826534.aspx
  Scopes & Permissions:
      http://msdn.microsoft.com/en-us/library/live/hh243646.aspx
  """
  def append_http_logs(self, message=""):
      http_logs = memcache.get("%s_http_logs"%(self.userid))
      if http_logs is None:
          http_logs = ""
      http_logs = "\n%s: %s\n" %(datetime.now(), message) + http_logs 
      memcache.set("%s_http_logs"%(self.userid), http_logs, 3600)
      logging.info("userid = %s, new message = %s "%(self.userid, message))
      

  def get_http_logs(self):
      return memcache.get("%s_http_logs"%(self.userid))

  def clear_http_logs(self,):
      memcache.delete("%s_http_logs"%(self.userid))
      
  def __init__(self, consumer_key, consumer_secret, callback_url):
    """Constructor."""
    self.access_token = None
    self.consumer_key = consumer_key
    self.consumer_secret = consumer_secret
    self.callback_url = callback_url
    self.userid = None
    self.scopes = ['wl.signin', 'wl.skydrive', 'wl.basic']
    

  def is_logined(self):
      return (self.access_token != None)

  def set_access_token(self, access_token):
      self.access_token = access_token
      pass
      
  def get_authorization_code_url(self):
    """Get Authorization URL."""
    auth_url = "https://oauth.live.com/authorize?client_id=%s&scope=%s&response_type=code&redirect_uri=%s" \
            % (self.consumer_key, '%20'.join(self.scopes), quote(self.callback_url))
    return auth_url


  def get_authorization_token(self, code):
    auth_url = "https://login.live.com/oauth20_token.srf"
    
    # prepare payloads
    params = {
      "client_id": self.consumer_key,
      "client_secret": self.consumer_secret,
      "grant_type": "authorization_code",
      "code": code,
      "redirect_uri": self.callback_url,
    }

    # headers
    headers = {
        "Content-type": "application/x-www-form-urlencoded"
    }
    
    response = self.make_request(auth_url, headers=headers, parameters=params,
                                 method=urlfetch.POST, protected=False)
    
    logging.info("get_authorization_token: response = {%s}" % (response.content))
    
    respjson = json.loads(response.content)
    if "access_token" in respjson:
        self.access_token = respjson["access_token"]
        self.access_token_expire_in = respjson["expires_in"]
        logging.info("get_authorization_token: get valid access_token = %s" % (self.access_token))
    self.authorization = respjson
    
    return self.authorization


  def make_request(self, url, headers={}, parameters={}, method=urlfetch.GET, protected=False):

    # compose payload & url
    payload = urlencode(parameters)
    if method == urlfetch.GET:
        url = "%s?%s" % (url, payload)
        payload = None
    logging.info("make_request: (%s, %s, %s)" % (method, url, payload))

    # headers
    if protected:
        headers["Authorization"] = "OAuth"
    #logging.info("make_request: headers" %(headers))
    
    rpc = urlfetch.create_rpc(deadline=10.0)
    self.append_http_logs("make_request: url={ %s }, payload={ %s }" %(url, payload))
    urlfetch.make_fetch_call(rpc, url, method=method, headers=headers, payload=payload)
    response = rpc.get_result()
    #logging.info("make_request: response = {%s}" % (response.content))

    return response
    
  def list_skydrive(self):
      if self.is_logined():
          api_url = "https://apis.live.net/v5.0/me/skydrive/files"
          params = {
                    "access_token": self.access_token
            }
          headers = {}
          response = self.make_request(api_url, headers=headers, parameters=params, method=urlfetch.GET, protected=False)
          logging.info("list_skydrive: response = %s" %(response.content))
          contents = json.loads(response.content)
          logging.info("contents: %s" %(json.dumps(contents, indent=4)))
          return contents['data']
      else:
          logging.info("list_skydrive: empty access_token, login required")
          return ""

  def list_folder(self, folder):
      if self.is_logined():
          # get parent id first
          api_url = "https://apis.live.net/v5.0/%s" %(folder)
          params = {
                    "access_token": self.access_token
            }
          headers = {}
          response = self.make_request(api_url, headers=headers, parameters=params, method=urlfetch.GET, protected=False)
          selfinfo = json.loads(response.content)
          parent_id = selfinfo["parent_id"]
          logging.info("list_folder parent id: %s" %(parent_id))
          
          # get child items
          api_url = "https://apis.live.net/v5.0/%s/files" %(folder)
          response = self.make_request(api_url, headers=headers, parameters=params, method=urlfetch.GET, protected=False)
          contents = json.loads(response.content)
          logging.info("list_folder contents: %s" %(json.dumps(contents, indent=4)))
          return (parent_id, contents['data'])
      else:
          logging.info("list_skydrive: empty access_token, login required")
          return ("", "")

  def get_file(self, file):
      if self.is_logined():
          # get content type
          api_url = "https://apis.live.net/v5.0/%s" %(file)
          params = {
                    "access_token": self.access_token
            }
          headers = {}
          response = self.make_request(api_url, headers=headers, parameters=params, method=urlfetch.GET, protected=False)
          contentinfo = json.loads(response.content)
          logging.info("get_file, response = %s" %(json.dumps(contentinfo, indent=4)))
          content_type = "image/jpeg"  # no way to find content type for live API? 
          updated_time = contentinfo['updated_time']
          logging.info("contents: content_type = %s, updated_time = %s" %(content_type, updated_time))
          
          # find data from memcache first
          _content_data = None
          _update_time = memcache.get(file)
          if _update_time is not None:
              if _update_time == updated_time:
                  # no modified after the cache
                  _content_data = memcache.get("%s_data" % (file))
          memcache.set(file, updated_time)
          logging.info("get_file, set update_time in memcach = %s" %(updated_time))
              
          # get content data
          if _content_data is None:
              api_url = "https://apis.live.net/v5.0/%s/content" %(file)
              response = self.make_request(api_url, headers=headers, parameters=params, method=urlfetch.GET, protected=False)
              _content_data = response.content
              logging.info("get_file: content length = %d" %(len(_content_data)))
              if len(_content_data) <= 1000000: 
                  memcache.set("%s_data" % (file), _content_data)
                  logging.info("get_file: save content data in memcache %s = %s" % ("%s_data" % (file), len(_content_data)))
          return (content_type, _content_data)
      else:
          logging.info("get_file: empty access_token, login required")
          return ("", "")   
    
  def lookup_user_info(self):
    """Lookup User Info.

    Lookup the user on Dropbox.
    """
    if self.is_logined():
        api_url = "https://apis.live.net/v5.0/me"
        params = {
                  "access_token": self.access_token,
        }
        headers = {
        }
        
        response = self.make_request(api_url, headers=headers, parameters=params,
                                 method=urlfetch.GET, protected=True)
        logging.info("lookup_user_info: response = {%s}" % (quote(response.content)))

        userinfo = json.loads(response.content)
        if "locale" in userinfo:
            logging.info("lookup_user_info: locale = %s" % (userinfo["locale"]))
        if "name" in userinfo:
            logging.info("lookup_user_info: name = %s" % (userinfo["name"].encode("gb2312", "ignore")))
            name = userinfo['name'].encode("gb2312", "ignore")
            logging.info( "type is: %s" %(type(name)) )
    
        return userinfo
    else:
        logging.info("lookup_user_info: access_token does not exist yet")
        return ""
